//----------------------------------------------------------------------------
// Copyright, 1991 .. 2001, by Mettler & Fuchs AG, Dietikon, Switzerland
//----------------------------------------------------------------------------
//
// Purpose:  Example Program 2 for RTX-51
//
//           Shows:  - task declaration with C language extensions
//                   - system startup
//                   - task creation
//                   - mailbox usage
//                   - use of tasks of different priority
//                   - task switching
//                   - message passing
//                   - memory pool creation
//                   - allocation of memory at predefined locations from
//                     within the program code
//
//           This program is intended as an introduction to working with
//           RTX-51.  Feel free to change it in any way helping you to
//           understand RTX-51 operation.
//
// File:     Example2.c
// Created:  1991 by --
// Modified: 2001-JUN-28 by P. Baessler
//
//----------------------------------------------------------------------------

// Includes ------------------------------------------------------------------

#include <Rtx51.h>
#include <string.h>
#include <stdio.h>


// Defines -------------------------------------------------------------------

//
// RTX constants
//
#define  RTX_DONE      0
#define  WAIT_FOREVER  0xFF

//
// Task numbers
//
// These numbers are used in the task declaration and identify the task code
// for task creation.
//
#define  PRODUCER_TASK  0  // Task number for the producer task.
#define  CONSUMER_TASK  1  // Task number for the consumer task.

//
// Task priorities
//
// The initial configuration of this example assigns to the consumer task a
// priority above the producer task. With this priority setting, each message
// produced by the producer will immediately get consumed by the consumer.
//
// Modify the priority constants below and observe what happens
//
// - if the producer priority is higher than the consumer priority
// - both producer and consumer have the same priority
//
#define  PRODUCER_PRIORITY  0
#define  CONSUMER_PRIORITY  1

//
// Mailbox
//
#define  MAILBOX  0


// Memory pools -------------------------------------------------------------

#define MAX_BLOCKS             3

#define POOL_A_BLOCK_SIZE    247
#define POOL_B_BLOCK_SIZE  0x430
#define POOL_C_BLOCK_SIZE  0x2830

#define POOL_A_SIZE  (8 * (2 + POOL_A_BLOCK_SIZE))
#define POOL_B_SIZE  (4 * (2 + POOL_B_BLOCK_SIZE))
#define POOL_C_SIZE  (2 * (2 + POOL_C_BLOCK_SIZE))

//
// The pools are created for application specific reasons at fixed memory
// locations, e.g. for memory mapped data access. This is not mandadory
// for standard applications.
//
unsigned char xdata  PoolA[POOL_A_SIZE] _at_ 0x5600;
unsigned char xdata  PoolB[POOL_B_SIZE] _at_ 0x5DC8;
unsigned char xdata  PoolC[POOL_C_SIZE] _at_ 0xAF00;


// Typedefs -----------------------------------------------------------------
typedef struct
{ 
   unsigned int  BlockSize;
   unsigned int  Contents;
} Message;


// Tasks --------------------------------------------------------------------

void  Producer (void) _task_ PRODUCER_TASK _priority_ PRODUCER_PRIORITY
//
// Purpose:        This task increments a counter and passes its value to the
//                 mailbox. This is the producer task.
//
{
   unsigned int xdata *  MessageToSend;
   static int xdata      BlockSelector = 0;
   static Message        TheMessage;

   for (;;)  // Endless loop of the task
   {
      //
      // Send the actual value of TheMessage to the mailbox until the
      // mailbox is full. This example uses memory blocks from different
      // pools. It passes pool information as first information within
      // TheMessage structure.
      //
      switch (BlockSelector)
      {
         case 0:
            MessageToSend = os_get_block (POOL_A_BLOCK_SIZE);
            TheMessage.BlockSize = POOL_A_BLOCK_SIZE;
            break; 
         case 1:
            MessageToSend = os_get_block (POOL_B_BLOCK_SIZE);
            TheMessage.BlockSize = POOL_B_BLOCK_SIZE;
            break; 
         case 2:
            MessageToSend = os_get_block (POOL_C_BLOCK_SIZE);
            TheMessage.BlockSize = POOL_C_BLOCK_SIZE;
            break; 
         default:
            //
            // This is the place to handle an unexpected 
            // BlockSelector exception.
            //
            break; 
      } // switch
      
      //
      // Increment the BlockSelector
      //
      BlockSelector = (BlockSelector + 1) % MAX_BLOCKS;  

      //
      // Check, whether a block was obtained from RTX.
      //
      if (NULL != MessageToSend)
      {
	     //
	     // Add data into the MessageToSendBuffer
	     //
	     memcpy (MessageToSend, &TheMessage, sizeof(TheMessage));
	     os_send_message (MAILBOX, MessageToSend, WAIT_FOREVER);
	     TheMessage.Contents++; // Increment TheMessage.Contents' value 
	                            // for the next transmission.
      }
      else
      {
         //
         // Raise an no free memory pool blocks exception.
         //
      } // if
   } // for
} // Producer


void  Consumer (void) _task_ CONSUMER_TASK _priority_ CONSUMER_PRIORITY
//
// Purpose: This task starts the message producer task and waits afterwards
//          for messages from the producer. It waits until a new message
//          arrives when the mailbox is empty.
//
{
   static Message xdata *  ReceivedMessage;
   unsigned int            Counter;
   unsigned char           RtxCompletion;


   //
   // Create the memory pools
   //
   RtxCompletion = os_create_pool (POOL_A_BLOCK_SIZE, &PoolA, POOL_A_SIZE);
   if (RTX_DONE == RtxCompletion)
   {
      RtxCompletion =
         os_create_pool (POOL_B_BLOCK_SIZE, &PoolB, POOL_B_SIZE);
      if (RTX_DONE == RtxCompletion)
      {
         RtxCompletion =
            os_create_pool (POOL_C_BLOCK_SIZE, &PoolC, POOL_C_SIZE);
         if (RTX_DONE == RtxCompletion)
       {
            //
            // Create the producer task, as soon as the memory pools are
            // available.
            //
            RtxCompletion = os_create_task (PRODUCER_TASK);
         }; // if
     }; // if
   }; // if

   if (RTX_DONE == RtxCompletion)
   {
      for (;;)
      {
         os_wait (K_MBX + MAILBOX, WAIT_FOREVER, (unsigned int xdata*)&ReceivedMessage);
         //
         // Process the message received ...
         //
         Counter = ReceivedMessage->Contents;
         //
         // Return the message block to the memory pool.
         //
         os_free_block (ReceivedMessage->BlockSize, ReceivedMessage);
      }; // for
   }
   else
   {
      for (;;);  // Producer task creation failed. Wait for doomsday.
   }; // if

} // Consumer


void  main (void)
//
// Purpose: The main function, which gets executed after the start-up code.
//          It starts the first RTX task and will not return from the
//          os_start_system call provided the task was successfully started.
//
{
   signed char  RtxCompletion;   // RTX completion code


   //
   // Start the real-time system
   //
   RtxCompletion = os_start_system (CONSUMER_TASK);

   //
   // The system could not get started or the memory pool creation failed,
   // when the program reaches here. Wait for doomsday.
   //
   for (;;);

} // main


